#pragma once

#include <array>
#include <project_config.h>
#include "stm32f4xx_ll_rcc.h"

#include <../../Utils/registers.hpp>
#include <fsmc.hpp>
#include <gpio.hpp>
#include <hal_delay.hpp>
#include <spi.hpp>
#include <stm32f4xx_hal.h>
#include <sys/types.h>

// ST LCD引脚接线
// 1：SD_CS      →
// 2：CTP_INT    →
// 3：CTP_SDA    → PB9
// 4：CTP_RST    → PB5
// 5：CTP_SCL    → PB8
// 6：MISO       → PC2
// 7：LED        →
// 8：SCK        → PB13
// 9：MOSI       → PC3
// 10：LCD_RS    → PC0
// 11：LCD_RST   → PB15
// 12：LCD_CS    → PB12
// 13：GND
// 14：VCC


// =========== 配置信息 ============
namespace bsp::lcd
{
    // ==================== 属性 ====================
    // 寄存器
    using TFT_CMD = utils::fsmcRegister<0x60060000>;// TFT命令寄存器片选地址
    using TFT_DATA = utils::fsmcRegister<0x60060002>;// TFT数据寄存器片选地址
    using TFT_RST = utils::fsmcRegister<0x60060004>;// TFT复位寄存器地址
    using TFT_LED = utils::fsmcRegister<0x60060008>;// TFT背光寄存器地址
    constexpr uint32_t TFT_DATA_ADDR = 0x60060002U;
    // 引脚
    using LCD_RST = hal::gpio::pinOld<GPIOB_BASE,GPIO_PIN_15>;// 复位引脚
    using LCD_RS = hal::gpio::pinOld<GPIOC_BASE,GPIO_PIN_0>;// 命令数据引脚 高电平为数据

    using lcd_spi = hal::spi2;

    // ==================== 属性声明 ====================
    // 接口类型
    enum class InterfaceType
    {
        Parallel8080,// 8080并口
        SPI_RS,// 包含RS线的SPI接口
        SPI,
    };

    // 设备类型
    enum class DeviceType
    {
        ILI9481,// 实验平台上的那块，只能8080并口
        ILI9488,// 实验平台上的那块，只能8080并口
        ST7796,//  自己买的，外接的触摸屏，只能SPI
        LT7680A,// 七寸屏幕
    };

    // DMA传输配置
    enum class DMAConfigType
    {
        disable,// 不使用DMA传输
        enable,// 使用DMA传输
    };

    enum class Rotation { Deg0, Deg90, Deg180, Deg270 };
}

// ==================== 函数声明 ====================


namespace bsp::lcd
{

    // 基础操作模板
    template<typename InterfacePolicy>
    struct LCDOperations
    {
        template<uint8_t cmd>
        static void write_cmd() { InterfacePolicy::template write_cmd<cmd>(); }

        template<uint8_t Data>
        static void write_data() { InterfacePolicy::template write_data<Data>(); }

        static void write_data(uint8_t Data) { InterfacePolicy::template write_data(Data); }

        template<size_t Ms>
        static void delay() { hal::delay::ms(Ms); }

        //========新增的命令类型==========//
        template<size_t Us>
        static void delay_us() { hal::delay::us(Us); }

        // 使用SFINAE检查是否存在read_data方法
        template<typename T = InterfacePolicy>
        static std::enable_if_t<std::is_same_v<decltype(T::read_data()), uint8_t>, uint8_t>
        read_data() { return T::read_data(); }

        // 使用SFINAE检查是否存在read_state方法
        template<typename T = InterfacePolicy>
        static std::enable_if_t<std::is_same_v<decltype(T::read_state()), uint8_t>, uint8_t>
        read_state() { return T::read_state(); }

    };

    // 命令序列生成器
    template<typename... Commands>
    struct CommandSequence
    {
        template<typename Ops>// 添加模板参数
        static void execute() { (Commands::template execute<Ops>(), ...); }
    };

    // 基本命令类型
    template<uint8_t Reg>
    struct RegCommand
    {
        template<typename Ops>
        static void execute() { Ops::template write_cmd<Reg>(); }
    };

    template<uint8_t Reg, uint8_t... Data>
    struct RegWithDataCommand
    {
        template<typename Ops>
        static void execute()
        {
            Ops::template write_cmd<Reg>();
            (Ops::template write_data<Data>(), ...);
        }
    };

    template<size_t Ms>
    struct DelayCommand
    {
        template<typename Ops>
        static void execute() { Ops::template delay<Ms>(); }
    };

    //========新增的命令类型==========//
    template<size_t Us>
    struct DelayUsCommand
    {
        template<typename Ops>
        static void execute() { Ops::template delay<Us>(); }
    };

    template<uint8_t delay, uint8_t Reg, uint8_t... Data>
    struct RegWithDataCommandUsDelay
    {
        template<typename Ops>
        static void execute()
        {
            Ops::template write_cmd<Reg>();
            DelayUsCommand<delay>();
            (Ops::template write_data<Data>(), ...);
        }
    };

    // 寄存器修改器模板
    template<uint8_t Reg, uint8_t SetMask, uint8_t ClearMask>
    struct RegModifyCommand
    {
        template<typename Ops>
        static void execute()
        {
            Ops::template write_cmd<Reg>();
            uint8_t temp = (Ops::template read_data() | SetMask) & ClearMask;// 读取当前值
            Ops::template write_data(temp);// 写回修改值
        }
    };

    /// SDRAM就绪状态检查命令 ///
    struct CheckSDRAMReadyCommand
    {
        template<typename Ops>
        static void execute()
        {
            uint8_t temp;
            do { temp = Ops::read_state(); } while ((temp & 0x04) == 0x00);
        }
    };


    // =========== 设备初始化序列特化 ============
    template<DeviceType T>
    struct InitSequence;

    // ============ ST7796初始化序列 ============
    template<>
    struct InitSequence<DeviceType::ST7796>
    {
        using type = CommandSequence<
            RegCommand<0x11>,// 退出睡眠模式
            DelayCommand<120>,
            RegWithDataCommand<0x36, 0x48>,// 屏幕方向设置（1<<6|1<<3）
            RegWithDataCommand<0x3A, 0x55>,// RGB565接口格式
            RegWithDataCommand<0xF0, 0xC3>,// 命令集控制1
            RegWithDataCommand<0xF0, 0x96>,// 命令集控制2
            RegWithDataCommand<0xB4, 0x01>,// 显示反转控制
            RegWithDataCommand<0xB7, 0xC6>,// 门驱动控制
            RegWithDataCommand<0xC0, 0x80, 0x45>,// 电源控制1（AVDD/VCL升压电压）
            RegWithDataCommand<0xC1, 0x13>,// 电源控制2（VGH/VGL电压）
            RegWithDataCommand<0xC2, 0xA7>,// 电源控制3（VCOM偏移）
            RegWithDataCommand<0xC5, 0x0A>,// VCOM控制（0x0A电压值）
            RegWithDataCommand<0xE8, 0x40, 0x8A, 0x00, 0x00, 0x29, 0x19, 0xA5, 0x33>,// 面板配置
            RegWithDataCommand<0xE0, 0xD0, 0x08, 0x0F, 0x06, 0x06, 0x33, 0x30, 0x33, 0x47, 0x17, 0x13, 0x13, 0x2B, 0x31>,// 正伽马校正
            RegWithDataCommand<0xE1, 0xD0, 0x0A, 0x11, 0x0B, 0x09, 0x07, 0x2F, 0x33, 0x47, 0x38, 0x15, 0x16, 0x2C, 0x32>,// 负伽马校正
            RegWithDataCommand<0xF0, 0x3C>,// 命令集控制3
            RegWithDataCommand<0xF0, 0x69>,// 命令集控制4 + 120ms延迟
            DelayCommand<120>,
            RegCommand<0x21>,// 反色关闭
            RegCommand<0x29>// 显示开启
        >;
    };

    template<>
    struct InitSequence<DeviceType::ILI9488>
    {
        using type = CommandSequence<
            RegCommand<0x11>,// 退出睡眠模式
            DelayCommand<5>,// 5ms延迟
            RegWithDataCommand<0xD0, 0x07, 0x47, 0x19>,// 电源控制1 (VCI1/VCI2/VC电压)
            RegWithDataCommand<0xD1, 0x00, 0x36, 0x1F>,// 电源控制2 (BT/VGH/VGL电压)
            RegWithDataCommand<0xD2, 0x01, 0x11>,// 电源控制3 (DC升压电路)
            RegWithDataCommand<0xE4, 0xA0>,// 驱动模式设置 (SS=1,GS=0)
            RegWithDataCommand<0xF3, 0x00, 0x2A>,// 帧速率控制 (60Hz)
            RegWithDataCommand<0xC0, 0x10, 0x3B, 0x00, 0x02, 0x11>,// MV偏压控制
            RegWithDataCommand<0xC5, 0x03>,// VCOM控制 (VCOMH=3.0V)
            RegWithDataCommand<0xC8, 0x00, 0x35, 0x23, 0x07, 0x00, 0x04, 0x45, 0x53, 0x77, 0x70, 0x00, 0x04>,// Gamma校正
            RegWithDataCommand<0x36, 0xE9>,// 显示方向控制 (MY=1,MX=0,MV=1,ML=1,BGR=1)
            RegWithDataCommand<0x3A, 0x55>,// 像素格式设置 (RGB565)
            RegCommand<0x20>,// 显存写入模式
            RegWithDataCommand<0x2A, 0x00, 0x00, 0x01, 0xDF>,// 水平地址设置 (0-479)
            RegWithDataCommand<0x2B, 0x00, 0x00, 0x01, 0x3F>,// 垂直地址设置 (0-319)
            DelayCommand<5>,// 5ms延迟
            RegCommand<0x29>,// 开启显示
            RegCommand<0x2C>,// 显存写入命令
            DelayCommand<5>// 5ms延迟
        >;
    };


    template<>
    struct InitSequence<DeviceType::ILI9481>
    {
        using type = CommandSequence<
            RegCommand<0x11>,// 退出睡眠模式
            DelayCommand<5>,// 5ms延迟
            RegWithDataCommand<0xD0, 0x07, 0x41, 0x18>,// 电源控制1 (VCI1/VCI2/VC电压)
            RegWithDataCommand<0xD1, 0x00, 0x0A, 0x10>,// 电源控制2 (BT/VGH/VGL电压)
            RegWithDataCommand<0xD2, 0x01, 0x11>,// 电源控制3 (DC升压电路)
            RegWithDataCommand<0xC0, 0x10, 0x3B, 0x00, 0x02, 0x11>,// 面板驱动控制
            RegWithDataCommand<0xC1, 0x10, 0x13, 0x88>,// 时序控制1
            RegWithDataCommand<0xC5, 0x02>,// VCOM控制 (VCOMH=2.0V)
            RegWithDataCommand<0xC8, 0x00, 0x37, 0x25, 0x06, 0x04, 0x1E, 0x26, 0x42, 0x77, 0x44, 0x0F, 0x12>,// Gamma校正
            RegWithDataCommand<0xF3, 0x40, 0x0A>,// 帧速率控制 (40Hz)
            RegWithDataCommand<0xF6, 0x80>,// 接口控制 (8080-I接口)
            RegWithDataCommand<0xF7, 0x80>,// 面板控制
            RegWithDataCommand<0x36, 0x2F>,// 显示方向 (MY=1,MX=0,MV=1,ML=1)
            RegWithDataCommand<0x3A, 0x55>,// 像素格式 (RGB565)
            RegCommand<0x20>,// 显存写入模式
            RegWithDataCommand<0x2A, 0x00, 0x00, 0x01, 0x3F>,// 水平地址设置 (0-319)
            RegWithDataCommand<0x2B, 0x00, 0x00, 0x01, 0xDF>,// 垂直地址设置 (0-479)
            RegWithDataCommand<0xC1, 0x00, 0x10, 0x22>,// 时序控制2
            DelayCommand<5>,// 5ms延迟
            RegCommand<0x29>,// 开启显示
            RegCommand<0x2C>,// 显存写入命令
            DelayCommand<20>// 20ms延迟
        >;


    };



    // =========== 接口策略模板 ===========
    template<InterfaceType T>
    struct InterfacePolicy;

    // SPI传输数据接口(带RS)
    template<>
    struct InterfacePolicy<InterfaceType::SPI_RS>
    {
        template<uint8_t cmd>
        static inline void write_cmd()
        {
            lcd_spi::cs_low();
            LCD_RS::clear();// 低电平发送命令
            lcd_spi::send_byte(cmd);
            lcd_spi::cs_high();
        }

        template<uint8_t data>
        static inline void write_data()
        {
            lcd_spi::cs_low();
            LCD_RS::set();// 高电平发送数据
            lcd_spi::send_byte(data);
            lcd_spi::cs_high();
        }

        //新增
        template<uint8_t tx_data>
        static inline uint8_t read_data()
        {
            uint8_t data;
            lcd_spi::cs_low();
            LCD_RS::set();// 数据模式
            lcd_spi::send_receive_byte(tx_data, data);// 发送0xFF，同时接收数据
            lcd_spi::cs_high();
            return data;
        }

        // -------- 运行时函数 --------
        static inline void write_cmd(const uint8_t cmd)
        {
            lcd_spi::cs_low();
            LCD_RS::clear();// 低电平发送命令
            lcd_spi::send_byte(cmd);
            lcd_spi::cs_high();
        }


        static inline void write_data(const uint8_t data)
        {
            lcd_spi::cs_low();
            LCD_RS::set();// 高电平发送数据
            lcd_spi::send_byte(data);
            lcd_spi::cs_high();
        }

        //稍作更改
        static inline uint8_t read_data(uint16_t tx_data = 0xFF)
        {
            uint8_t data;
            lcd_spi::cs_low();
            LCD_RS::set();// 数据模式
            lcd_spi::send_receive_byte(tx_data, data);// 发送0xFF，同时接收数据
            lcd_spi::cs_high();
            return data;
        }
    };


    // 8080并口传输数据接口
    template<>
    struct InterfacePolicy<InterfaceType::Parallel8080>
    {
        template<uint8_t cmd>
        static inline void write_cmd() { TFT_CMD::write(cmd); }

        template<uint16_t data>
        static inline void write_data() { TFT_DATA::write(data); }

        static inline void write_cmd(const uint8_t cmd) { TFT_CMD::write(cmd); }

        static inline void write_data(const uint16_t data) { TFT_DATA::write(data); }
    };


    // =========== 方向控制模板 ===========
    template<DeviceType Dev, InterfaceType Interface>
    struct RotationControl;

    template<>
    struct RotationControl<DeviceType::ILI9488, InterfaceType::Parallel8080>
    {
        static void set(Rotation rot)
        {
            constexpr uint8_t params[] = {0xE9, 0x29, 0x69, 0xA9};
            InterfacePolicy<InterfaceType::Parallel8080>::write_cmd(0x36);
            InterfacePolicy<InterfaceType::Parallel8080>::write_data(params[static_cast<int>(rot)]);
        }
    };

    template<>
    struct RotationControl<DeviceType::ST7796, InterfaceType::SPI_RS>
    {
        // 注意：原始方向为竖屏
        //     位	名称	描述
        //     7	    MY	垂直方向镜像（1：镜像，0：正常）
        //     6	    MX	水平方向镜像（1：镜像，0：正常）
        //     5	    MV	行列交换（1：交换，0：不交换）
        //     4   	ML	垂直刷新顺序（1：反向，0：正常）
        //     3 	BGR	RGB/BGR 顺序（1：BGR，0：RGB）
        //     2 	MH	水平刷新顺序（1：反向，0：正常）
        //     1 	-	保留位
        //     0 	-	保留位
        static void set(Rotation rot)
        {
            constexpr uint8_t params[] = {0xE8, 0x48, 0x28, 0x88};
            InterfacePolicy<InterfaceType::SPI_RS>::write_cmd(0x36);
            InterfacePolicy<InterfaceType::SPI_RS>::write_data(params[static_cast<int>(rot)]);
        }
    };

    template<>
    struct RotationControl<DeviceType::LT7680A, InterfaceType::SPI>
    {
        // 旋转与镜像组合枚举
        enum RotationMode
        {
            Mode1,// 0° (默认)
            Mode2,// 水平镜像
            Mode3,// 90°旋转 + 水平镜像
            Mode4,// 270°旋转
            Mode5,// 垂直镜像
            Mode6,// 180°旋转
            Mode7,// 270°旋转 + 水平镜像
            Mode8// 90°旋转
        };

        // 设置旋转与镜像模式
        static void setRotation(RotationMode mode)
        {
            using spi = InterfacePolicy<InterfaceType::SPI_RS>;
            // 设置内存写入方向
            spi::write_cmd(0x02);
            uint8_t temp = spi::read_data();
            temp &= 0xF9;// 清除bit1-2 (0xF9 = 11111001b)

            switch (mode) {
                case Mode1:
                case Mode5:
                    // MemWrite_Left_Right_Top_Down
                    break;
                case Mode2:
                case Mode6:
                    // MemWrite_Right_Left_Top_Down
                    temp |= 0x02;// 设置bit1
                    break;
                case Mode3:
                case Mode7:
                    // MemWrite_Top_Down_Left_Right
                    temp |= 0x04;// 设置bit2
                    break;
                case Mode4:
                case Mode8:
                    // MemWrite_Down_Top_Left_Right
                    temp |= 0x06;// 设置bit1+2
                    break;
            }
            spi::write_data(temp);

            // 设置垂直扫描方向
            spi::write_cmd(0x12);
            temp = spi::read_data();
            temp &= 0xF7;// 清除bit3 (0xF7 = 11110111b)

            if (mode >= Mode5) {
                // Mode5-8: 垂直镜像 (VSCAN_B_to_T)
                temp |= 0x08;// 设置bit3
            }
            spi::write_data(temp);
        }

        // 便捷函数：设置常见旋转角度
        static void set(Rotation rot)
        {
            switch (rot) {
                case Rotation::Deg0:
                    setRotation(Mode1);
                    break;
                case Rotation::Deg90:
                    setRotation(Mode8);
                    break;
                case Rotation::Deg180:
                    setRotation(Mode6);
                    break;
                case Rotation::Deg270:
                    setRotation(Mode4);
                    break;
            }
        }
    };

}


// ==================== 导出接口 ====================
namespace bsp::lcd
{

    template<void(*delay_ms)(uint32_t), DeviceType DevType, InterfaceType Interface = InterfaceType::Parallel8080,
             DMAConfigType DMAConfig = DMAConfigType::disable>
    class LCDController
    {
        using Policy = InterfacePolicy<Interface>;
        using Ops = LCDOperations<Policy>;
        using InitSeq = typename InitSequence<DevType>::type;
        using RotCtrl = RotationControl<DevType, Interface>;

    public:
        static void init()
        {
            // 硬件复位
            if constexpr (Interface == InterfaceType::Parallel8080) {
                // FSMC因为FPGA也要使用的原因，因此提前初始化了

                // 复位TFT显示屏
                TFT_LED::write(0);// 关闭背光LED
                TFT_RST::write(1);// 将TFT复位引脚设为高电平
                delay_ms(5);// 等待100毫秒
                TFT_RST::write(0);// 将TFT复位引脚设为低电平
                delay_ms(5);// 等待100毫秒
                TFT_RST::write(1);// 将TFT复位引脚设为高电平，完成复位操作
                delay_ms(5);// 等待100毫秒
            }
            else if constexpr (Interface == InterfaceType::SPI_RS ) {
                using namespace hal::gpio;
                // 初始化SPI2
                lcd_spi::init();

                LCD_RST::init<mode::output_pp, pull::none, speed::very_high>();
                LCD_RST::set();
                if constexpr (Interface == InterfaceType::SPI_RS) { LCD_RS::init<mode::output_pp, pull::none, speed::very_high>(); }
                //LCD 复位
                delay_ms(30);
                LCD_RST::clear();
                delay_ms(100);
                LCD_RST::set();
                delay_ms(50);
            }

            if constexpr(Interface == InterfaceType::SPI) {
                // LT768_Init();
            }else {
                // 执行编译期生成的初始化序列
                InitSeq::template execute<Ops>();

                if constexpr (Interface == InterfaceType::Parallel8080) TFT_LED::write(1);// 开启背光LCD
                set_rotation(Rotation::Deg0);
            }



        }

        /**
         * 设置LCD显示方向
         * 该函数用于设置LCD的显示方向，通过指定显示方向参数来确定LCD的显示方式
         * @param rot 显示方向参数，取值范围为0~3，分别表示0度、90度、180度、270度
         */
        static void set_rotation(const Rotation rot) { RotCtrl::set(rot); }

        static void flush(const int16_t x1, const int16_t y1, const int16_t x2, const int16_t y2, uint8_t *colors)
        {
            if constexpr ((Interface == InterfaceType::SPI_RS && DMAConfig == DMAConfigType::enable) | (Interface == InterfaceType::SPI && DMAConfig == DMAConfigType::enable)) {
                // 使用DMA的情况
                while (HAL_DMA_GetState(lcd_spi::getDMAHandle_tx()) != HAL_DMA_STATE_READY) {}
            }

            set_window(x1, y1, x2, y2);

            const uint32_t pixel_count = (x2 - x1 + 1) * (y2 - y1 + 1);// 计算像素数量

            if constexpr (Interface == InterfaceType::Parallel8080) {
                // ====================== 8080并口接口 ======================
                if constexpr (DMAConfig == DMAConfigType::disable) {
                    // 不使用DMA
                    for (uint32_t i = 0; i < pixel_count; ++i) { TFT_DATA::write(colors[i]); }
                }
                else {
                    // 使用DMA传输数据到LCD
                    // HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream6, (uint32_t) color_p, TFT_DATA_ADDR,
                    //      ((x2 + 1) - x1) * ((y2 + 1) - y1));
                }
            }
            else if constexpr (Interface == InterfaceType::SPI_RS) {
                // ====================== SPI接口 ======================
                LCD_RS::set();
                lcd_spi::cs_low();

                if constexpr (DMAConfig == DMAConfigType::disable) {
                    lcd_spi::send_bytes(colors, pixel_count * 2);
                    lcd_spi::cs_high();// 禁用LCD片选
                }
                else {
                    // ====== 使用DMA =====
                    HAL_DMA_Start_IT(lcd_spi::getDMAHandle_tx(),
                                     reinterpret_cast<uint32_t>(colors),// 直接使用uint16_t*地址
                                     reinterpret_cast<uint32_t>(&(lcd_spi::getHandle()->Instance->DR)),// SPI数据寄存器地址
                                     pixel_count << 1);

                    // SPI2向DMA发出请求
                    lcd_spi::getDMAHandle_rx()->Instance->CR |= DMA_IT_TC;
                }
            }
            else if constexpr (Interface == InterfaceType::SPI) {


            }
        }

    private:
        static void set_window(const uint16_t x1, const uint16_t y1, const uint16_t x2, const uint16_t y2)
        {
            Policy::write_cmd(0x2A);
            Policy::write_data(x1 >> 8);
            Policy::write_data(x1 & 0xFF);
            Policy::write_data(x2 >> 8);
            Policy::write_data(x2 & 0xFF);

            Policy::write_cmd(0x2B);
            Policy::write_data(y1 >> 8);
            Policy::write_data(y1 & 0xFF);
            Policy::write_data(y2 >> 8);
            Policy::write_data(y2 & 0xFF);

            Policy::template write_cmd<0x2C>();// 开始传输数据到LCD
        }
    };
}